﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace CrayonsBox.Drawing
{
    public class Utils
    {
        private const int KEY_PRESSED = 0x8000;
        
        private const int VK_CONTROL = 0x11;

        [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
        public static extern void CopyMemory(IntPtr Destination, IntPtr Source, Int32 Length);

        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);

        [DllImport("user32.dll")]
        static extern short GetKeyState(int key);

        public static bool IsControlKeyDown()
        {
            return (GetKeyState(VK_CONTROL) & KEY_PRESSED) != 0;
        }

        public static BitmapSource CreateBitmapSourceFromBitmap(Bitmap bitmap)
        {
            if (bitmap == null)
                throw new ArgumentNullException("bitmap");

            IntPtr hBitmap = bitmap.GetHbitmap();

            try
            {
                return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                    hBitmap,
                    IntPtr.Zero,
                    System.Windows.Int32Rect.Empty,
                    BitmapSizeOptions.FromEmptyOptions());
            }
            finally
            {
                DeleteObject(hBitmap);
            }
        }

        public static byte[] Scale_NearestNeighbor(byte[] srcPixels, Size srcSize, Size destSize)
        {
            byte[] res = new byte[4 * destSize.Height * destSize.Width];
            double h_ratio = (double)srcSize.Height / (double)destSize.Height;
            double w_ratio = (double)srcSize.Width / (double)destSize.Width;

            for (int i = 0; i < destSize.Height; i++)
                for (int j = 0; j < destSize.Width; j++)
                {
                    int px = (int)Math.Floor(j * w_ratio);
                    int py = (int)Math.Floor(i * h_ratio);
                    res[4 * ((i * destSize.Width) + j)] = srcPixels[4 * ((py * srcSize.Width) + px)];
                    res[4 * ((i * destSize.Width) + j) + 1] = srcPixels[4 * ((py * srcSize.Width) + px) + 1];
                    res[4 * ((i * destSize.Width) + j) + 2] = srcPixels[4 * ((py * srcSize.Width) + px) + 2];
                    res[4 * ((i * destSize.Width) + j) + 3] = srcPixels[4 * ((py * srcSize.Width) + px) + 3];
                }
            return res;
        }

        public static byte[] Scale_Bilinear(byte[] srcPixels, Size srcSize, Size destSize)
        {
            byte[] res = new byte[4 * destSize.Width * destSize.Height];
            byte a, b, c, d, x, y, index;
            double x_ratio = (double)(srcSize.Width) / (double)destSize.Width;
            double y_ratio = (double)(srcSize.Height) / (double)destSize.Height;
            double x_diff, y_diff;
            int offset = 0;

            for (int i = 0; i < destSize.Height; i++)
                for (int j = 0; j < destSize.Width; j++)
                {
                    x = (byte)(x_ratio * j);
                    y = (byte)(y_ratio * i);
                    x_diff = (x_ratio * j) - x;
                    y_diff = (y_ratio * i) - y;
                    index = (byte)(y * srcSize.Width + x);
                    byte ar, ag, ab;
                    byte br, bg, bb;
                    byte cr, cg, cb;
                    byte dr, dg, db;
                    // Get A point
                    ab = srcPixels[4 * index];
                    ag = srcPixels[4 * index + 1];
                    ar = srcPixels[4 * index + 2];
                    a = srcPixels[4 * index + 3]; // alpha
                    // Get B point
                    bb = srcPixels[4 * (index + 1)];
                    bg = srcPixels[4 * (index + 1) + 1];
                    br = srcPixels[4 * (index + 1) + 2];
                    b = srcPixels[4 * (index + 1) + 3]; // alpha
                    // Get C point
                    cb = srcPixels[4 * (index + srcSize.Width)];
                    cg = srcPixels[4 * (index + srcSize.Width) + 1];
                    cr = srcPixels[4 * (index + srcSize.Width) + 2];
                    c = srcPixels[4 * (index + srcSize.Width) + 3]; // alpha
                    // Get D point
                    db = srcPixels[4 * (index + srcSize.Width + 1)];
                    dg = srcPixels[4 * (index + srcSize.Width + 1) + 1];
                    dr = srcPixels[4 * (index + srcSize.Width + 1) + 2];
                    d = srcPixels[4 * (index + srcSize.Width + 1) + 3]; // alpha

                    // blue
                    res[4 * offset] = (byte)(ab * (1 - x_diff) * (1 - y_diff) + bb * x_diff * (1 - y_diff) + cb * y_diff * (1 - x_diff) + db * x_diff * y_diff);
                    // green
                    res[4 * offset + 1] = (byte)(ag * (1 - x_diff) * (1 - y_diff) + bg * x_diff * (1 - y_diff) + cg * y_diff * (1 - x_diff) + dg * x_diff * y_diff);
                    // red
                    res[4 * offset + 2] = (byte)(ar * (1 - x_diff) * (1 - y_diff) + br * x_diff * (1 - y_diff) + cr * y_diff * (1 - x_diff) + dr * x_diff * y_diff);
                    // alpha
                    res[4 * offset + 3] = (byte)(a * (1 - x_diff) * (1 - y_diff) + b * x_diff * (1 - y_diff) + c * y_diff * (1 - x_diff) + d * x_diff * y_diff);

                    offset++;
                }
            return res;
        }

        public static void Blend_AlphaBlending(byte[] srcBgra, Point srcPos, Size srcSize, ref byte[] desBgra, Size desSize)
        {
            for (int i = 0; i < srcSize.Height && i < desSize.Height; i++)
            {
                for (int j = 0; j < srcSize.Width && j < desSize.Width; j++)
                {
                    int idx0 = 4 * (i * srcSize.Width + j);
                    int idx1 = 4 * ((srcPos.Y + i) * desSize.Width + (srcPos.X + j));

                    if ((srcPos.X + j) >= (int)desSize.Width
                        || idx0 >= (int)srcBgra.Length || idx0 < 0
                        || idx1 >= (int)desBgra.Length || idx1 < 0) continue;

                    //blend pixels - alpha blending
                    float srca = (float)srcBgra[idx0 + 3] / 255.0f;
                    desBgra[idx1] = (byte)(srcBgra[idx0] * srca + desBgra[idx1] * (1.0f - srca));
                    desBgra[idx1 + 1] = (byte)(srcBgra[idx0 + 1] * srca + desBgra[idx1 + 1] * (1.0f - srca));
                    desBgra[idx1 + 2] = (byte)(srcBgra[idx0 + 2] * srca + desBgra[idx1 + 2] * (1.0f - srca));
                    desBgra[idx1 + 3] = (byte)(srcBgra[idx0 + 3] * srca); // + desBgra[idx1 + 3] * (1.0f - srca)); // not sure why should we add old desBrga alpha
                }
            }
        }
    }
}
